///\todo upgrade

//////////////////////////////////////////////////////////////////////////////////
//                                                                              //
//  This file is part of the buola project (https://code.google.com/p/buola/).  //
//                                                                              //
//  Copyright(c) 2007-2012 Xavi Gratal                                          //
//  gratal AT gmail DOT com                                                     //
//                                                                              //
//  Buola is free software: you can redistribute it and/or modify               //
//  it under the terms of the GNU General Public License as published by        //
//  the Free Software Foundation, either version 3 of the License, or           //
//  (at your option) any later version.                                         //
//                                                                              //
//  Buola is distributed in the hope that it will be useful,                    //
//  but WITHOUT ANY WARRANTY; without even the implied warranty of              //
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               //
//  GNU General Public License for more details.                                //
//                                                                              //
//  You should have received a copy of the GNU General Public License           //
//  along with buola.  If not, see <http://www.gnu.org/licenses/>.              //
//                                                                              //
//////////////////////////////////////////////////////////////////////////////////

#ifndef _BUOLA_IMAGE_ALGORITHM_DETAIL_OPENCV_H_
#define _BUOLA_IMAGE_ALGORITHM_DETAIL_OPENCV_H_

#include <buola/image/format.h>

#include <opencv2/imgproc/imgproc_c.h>
#include <opencv2/calib3d/calib3d.hpp>
#include <opencv2/video/tracking.hpp>

#include <buola/mat.h>

namespace buola { namespace img {

//! \name wrappers
//@{

//! helper class for conversion between buola and opencv images
template<typename tFormat>
struct MFormatToCVType;

template<>
struct MFormatToCVType<img::TFormat_rgb8>
{
    static const int mMatType=CV_8UC3;
};

template<>
struct MFormatToCVType<img::TFormat_gray8>
{
    static const int mMatType=CV_8UC1;
};

template<>
struct MFormatToCVType<img::TFormat_gray16>
{
    static const int mMatType=CV_16UC1;
};

template<>
struct MFormatToCVType<img::TFormat_gray16s>
{
    static const int mMatType=CV_16SC1;
};

template<>
struct MFormatToCVType<img::TFormat_gray32s>
{
    static const int mMatType=CV_32SC1;
};

template<>
struct MFormatToCVType<img::TFormat_gray32f>
{
    static const int mMatType=CV_32FC1;
};

template<>
struct MFormatToCVType<img::TFormat_rgb32f>
{
    static const int mMatType=CV_32FC3;
};

template<>
struct MFormatToCVType<img::TFormat_xyz32f>
{
    static const int mMatType=CV_32FC3;
};

template<typename tFormat>
inline const cv::Mat mat_wrap(const CView<tFormat> &pView)
{
    return cv::Mat(pView.Height(),pView.Width(),MFormatToCVType<tFormat>::mMatType,
                   const_cast<uint8_t*>(pView.RawBytes()),pView.Stride());
}

/*namespace img*/ }

namespace mat {

template<typename tScalar>
struct MScalarToCVType;

template<>
struct MScalarToCVType<double>
{
    static const int mMatType=CV_64FC1;
};

template<>
struct MScalarToCVType<float>
{
    static const int mMatType=CV_32FC1;
};

template<>
struct MScalarToCVType<int>
{
    static const int mMatType=CV_32SC1;
};

namespace detail {

    template<typename tScalar>
    class CMatWrapper : public cv::_OutputArray
    {
    public:
        CMatWrapper(mat::CMat<tScalar> &pMat)
            :   _OutputArray(mCV)
            ,   mMat(pMat)
            ,   mCV(pMat.Rows(),pMat.Cols(),MScalarToCVType<tScalar>::mMatType,
                             const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(pMat.Data())),pMat.Stride())
        {
            flags&=~FIXED_SIZE;
        }
        
        virtual void create(int pRows,int pCols,int pType,int i,bool pAllowTransposed,int pFixedDepth) const
        {
            if(pType!=MScalarToCVType<tScalar>::mMatType) throw XInvalid("invalid result type in opencv wrapper");
            if(i>=0) throw XInvalid("invalid index in opencv wrapper");
            if(pRows==mMat.Rows()&&pCols==mMat.Cols()) return;
            
            mMat.Resize(pRows,pCols);
            mCV=cv::Mat(mMat.Rows(),mMat.Cols(),MScalarToCVType<tScalar>::mMatType,
                                    const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(mMat.Data())),mMat.Stride());
        }
        
        virtual void create(cv::Size pSize,int pType,int i,bool pAllowTransposed,int pFixedDepth) const
        {
            create(pSize.height,pSize.width,pType,i,pAllowTransposed,pFixedDepth);
        }
        
        virtual void create(int pNDims,const int *pSizes,int pType,int i,bool pAllowTransposed,int pFixedDepth) const
        {
            if(pNDims!=2) throw XInvalid("invalid number of dimensions in opencv wrapper");
            create(pSizes[0],pSizes[1],pType,i,pAllowTransposed,pFixedDepth);
        }
        
        virtual ~CMatWrapper() {}
        
        operator const cv::Mat&()   {   return mCV; }
        
    private:
        mat::CMat<tScalar> &mMat;
        mutable cv::Mat mCV;
    };

/*namespace detail*/ }

///allows using a buola matrix as an opencv matrix
template<typename tScalar>
inline detail::CMatWrapper<tScalar> mat_wrap(mat::CMat<tScalar,cDynamic,cDynamic> &pMat)
{
    return pMat;
}

///allows using a buola matrix as an opencv matrix
template<typename tScalar,size_t vRows,size_t vCols>
inline const cv::Mat mat_wrap(const mat::CMat<tScalar,vRows,vCols> &pMat)
{
    return cv::Mat(pMat.Rows(),pMat.Cols(),MScalarToCVType<tScalar>::mMatType,
                   const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(pMat.Data())),pMat.Stride());
}

//@}
    
/*namespace mat*/ } /*namespace buola*/ }

#endif
